/**
 * 5.[Browser part]: Jet.event 扩展包
 */
Jet().$package(function(J){
	var $E,
		addEventListener,
		removeEventListener,
		onDomReady,
		isDomReady,
		Publish,
		addObserver,
		notifyObservers,
		removeObserver;
	
	/**
	 * event 名字空间
	 * 
	 * @namespace
	 * @name event
	 */
	
	J.event = J.event || {};
	$E = J.event;
	
	/*
	 	经典的彩蛋必备代码:老外称之为 Tweetable Konami code
		[上上下下左右左右BA]
		var k=[];
		addEventListener("keyup",function(e){ 
		   k.push(e.keyCode);
		   if(k.toString().indexOf("38,38,40,40,37,39,37,39,66,65")>=0){      
		       cheat();
		   }
		},true);
		
		什么不知道 Konami Code? 只能说明你没童年了 - -!
		http://en.wikipedia.org/wiki/Konami_Code
	 */
	
	// From: David Flanagan.
	// In DOM-compliant browsers, our functions are trivial wrappers around
	// addEventListener( ) and removeEventListener( ).
	if (document.addEventListener) {
		/**
		 * 
		 * 添加事件监听器
		 * 
		 * @method addEventListener
		 * @memberOf Event
		 * 
		 * @param element 元素
		 * @param eventType 事件类型，不含on
		 * @param handler 事件处理器
		 * @return {Element} 返回元素
		 */
		addEventListener = function(element, eventType, handler) {
			//var id = $E._uid( );  // Generate a unique property name
			var isExist = false;
			if(!element){
				J.out('targetModel undefined:'+eventType+handler);
			}
			if(!element._eventTypes){
				element._eventTypes = {};
			}
			if (!element._eventTypes[eventType]){
				element._eventTypes[eventType] = [];
			}
	        element.addEventListener(eventType, handler, false);
	        
	        var handlers= element._eventTypes[eventType];
	        for(var i=0; i<handlers.length; i++){
	        	if(handlers[i] == handler){
	        		isExist = true;
	        	}
	        }
	        if(!isExist){
	        	handlers.push(handler);
	        }
		};
		
		/**
		 * 
		 * 移除事件监听器
		 * 
		 * @memberOf event
		 * @method removeEventListener
		 * 
		 * @param element 元素
		 * @param eventType 事件类型，不含on
		 * @param handler 事件处理器
		 * @return {Element} 返回元素
		 */
		removeEventListener = function(element, eventType, handler) {
			if(eventType){
				if(handler){
					element.removeEventListener(eventType, handler, false);
					if(element._eventTypes && element._eventTypes[eventType]){
						var handlers = element._eventTypes[eventType];
						for(var i=0; i<handlers.length; i++){
							if(handlers[i] === handler){
								handlers[i]=null;
								handlers.splice(i, 1);
								break;
							}
						}
					}
				}else{
					
					if(element._eventTypes && element._eventTypes[eventType]){
						var handlers = element._eventTypes[eventType];
						
						for(var i=0; i<handlers.length; i++){
							element.removeEventListener(eventType, handlers[i], false);
						}
						element._eventTypes[eventType] = [];
					}
					
				}
			}else{
				if(element._eventTypes){
					var eventTypes = element._eventTypes;
					for(var p in eventTypes){
						var handlers = element._eventTypes[p];
						for(var i=0; i<handlers.length; i++){
							element.removeEventListener(p, handlers[i], false);
						}
					}
					eventTypes = {};
				}
			}
			
		};
	}
	// In IE 5 and later, we use attachEvent( ) and detachEvent( ), with a number of
	// hacks to make them compatible with addEventListener and removeEventListener.
	else if (document.attachEvent) {
		/**
		 * 兼容ie的写法
		 * @ignore
		 */
		addEventListener = function(element, eventType, handler) {

			if ($E._find(arguments) != -1){
				return;
			}
		
			var wrappedEvent = function(e){
				if(!e){
					e = window.event;
				}

				var event = {
					_event: e,// In case we really want the IE event object
					
					type: e.type,           // Event type
	                target: e.srcElement,   // Where the event happened
	                currentTarget: element, // Where we're handling it
	                relatedTarget: e.fromElement ? e.fromElement : e.toElement,
	                eventPhase: (e.srcElement == element) ? 2 : 3,
	
	                // Mouse coordinates
	                clientX: e.clientX,
					clientY: e.clientY,
	                screenX: e.screenX,
					screenY: e.screenY,
					layerX: e.offsetX,
					layerY: e.offsetY,
					pageX: e.clientX + document.body.scrollLeft,
					pageY: e.clientY + document.body.scrollTop,
	                
	               // Key state
	                altKey: e.altKey,
					ctrlKey: e.ctrlKey,
	                shiftKey: e.shiftKey,
	                //原有的charCode
					charCode: e.keyCode,
					//keyCode
					keyCode: e.keyCode,
					/*
					 * keyCode 值附表：
					 * ===============================
					 * 
					 * 1.主键盘区字母和数字键的键码值
					 * 按键 	键码
					 * 0 	48
					 * 1 	49
					 * 2 	50
					 * 3 	51
					 * 4 	52
					 * 5 	53
					 * 6 	54
					 * 7 	55
					 * 8 	56
					 * 9 	57
					 * 
					 * A 	65
					 * B 	66
					 * C 	67
					 * D 	68
					 * E 	69
					 * F 	70
					 * G 	71
					 * H 	72
					 * I 	73
					 * J 	74
					 * K 	75
					 * L 	76
					 * M 	77
					 * N 	78
					 * O 	79
					 * P 	80
					 * Q 	81
					 * R 	82
					 * S 	83
					 * T 	84
					 * U 	85
					 * V 	86
					 * W 	87
					 * X 	88
					 * Y 	89
					 * Z 	90
					 * 
					 * 
					 * 3.控制键键码值
					 * 按键			键码
					 * BackSpace	8
					 * Tab			9
					 * Clear		12
					 * Enter		13
					 * Shift		16
					 * Control		17
					 * Alt			18
					 * Cape Lock	20
					 * Esc			27
					 * Spacebar		32 
					 * Page Up		33
					 * Page Down	34
					 * End			35
					 * Home			36
					 * Left Arrow	37
					 * Up Arrow 	38
					 * Right Arrow	39
					 * Down Arrow	40
					 * Insert		45
					 * Delete		46
					 * 
					 * Num Lock		144
					 * 
					 * ;:			186
					 * =+			187
					 * ,<			188
					 * -_			189
					 * .>			190
					 * /?			191
					 * `~			192
					 * 
					 * [{			219
					 * \|			220
					 * }]			221
					 * ’"			222
					 * 
					 * 2.功能键键码值
					 * F1 	112
					 * F2 	113
					 * F3 	114
					 * F4 	115
					 * F5 	116
					 * F6 	117
					 * F7 	118
					 * F8 	119
					 * F9 	120
					 * F10 	121
					 * F11 	122
					 * F12 	123
					 * 
					 * 2.数字键盘上的键的键码值
					 * 按键 	键码
					 * 0 	96
					 * 1 	97
					 * 2 	98
					 * 3 	99
					 * 4 	100
					 * 5 	101
					 * 6 	102
					 * 7 	103
					 * 8 	104
					 * 9 	105
					 * 
					 * * 	106
					 * + 	107
					 * Enter108
					 * - 	109
					 * . 	110
					 * / 	111
					 * 
					 */

	                stopPropagation: function(){
	                	this._event.cancelBubble = true;
	                },
	                preventDefault: function(){
	                	this._event.returnValue = false;
	                }
	            }
	

	            if (Function.prototype.call){
	                handler.call(element, event);
	            }else {
	                // If we don't have Function.call, fake it like this.
	                element._currentHandler = handler;
	                element._currentHandler(event);
	                element._currentHandler = null;
	            }
	        };
	
	        // Now register that nested function as our event handler.
	        element.attachEvent("on" + eventType, wrappedEvent);
	

	        var h = {
	            element: element,
	            eventType: eventType,
	            handler: handler,
	            wrappedEvent: wrappedEvent
	        };
	

	        var d = element.document || element;
	        // Now get the window associated with that document.
	        var w = d.parentWindow;
	
	        // We have to associate this handler with the window,
	        // so we can remove it when the window is unloaded.
	        var id = $E._uid( );  // Generate a unique property name
	        if (!w._allHandlers) w._allHandlers = {};  // Create object if needed
	        w._allHandlers[id] = h; // Store the handler info in this object
	
	        // And associate the id of the handler info with this element as well.
	        if (!element._handlers) element._handlers = [];
	        element._handlers.push(id);
	
	        // If there is not an onunload handler associated with the window,
	        // register one now.
	        if (!w._onunloadEventRegistered) {
	            w._onunloadEventRegistered = true;
	            w.attachEvent("onunload", $E._removeAllEvents);
	        }
	    };
		
	    /**
		 * 兼容ie的写法
		 * @ignore
		 */
	    removeEventListener = function(element, eventType, handler) {
	    	
	        // Find this handler in the element._handlers[] array.
	        var handlersIndex = $E._find(arguments);
	        if (handlersIndex == -1) return;  // If the handler was not registered, do nothing
	        // Get the window of this element.
	        var d = element.document || element;
	        var w = d.parentWindow;
			for(var j=0; j<handlersIndex.length; j++){
				var i = handlersIndex[j];
		        // Look up the unique id of this handler.
		        var handlerId = element._handlers[i];
		        // And use that to look up the handler info.
		        var h = w._allHandlers[handlerId];
		        // Using that info, we can detach the handler from the element.
		        element.detachEvent("on" + h.eventType, h.wrappedEvent);
		        // Remove one element from the element._handlers array.
				element._handlers[i]=null;
		        element._handlers.splice(i, 1);
		        // And delete the handler info from the per-window _allHandlers object.
		        delete w._allHandlers[handlerId];
			}
			if(element._handlers && element._handlers.length==0){
				element._handlers=null;
			}
	    };
	
	    // A utility function to find a handler in the element._handlers array
	    // Returns an array index or -1 if no matching handler is found
	    $E._find = function(args) {
	    	var element = args[0],
				eventType = args[1],
				handler = args[2],
				handlers = element._handlers;
				
	        if (!handlers){
	        	return -1;  // if no handlers registered, nothing found
	        }
	
	        // Get the window of this element
	        var d = element.document || element;
	        var w = d.parentWindow;
	
	        var handlersIndex = [];

			if(args.length === 3){
				// Loop through the handlers associated with this element, looking
		        // for one with the right type and function.
		        // We loop backward because the most recently registered handler
		        // is most likely to be the first removed one.
		        for(var i = handlers.length-1; i >= 0; i--) {
		            var handlerId = handlers[i];        // get handler id
		            var h = w._allHandlers[handlerId];  // get handler info
		            // If handler info matches type and handler function, we found it.
		            if (h.eventType == eventType && h.handler == handler){
		            	handlersIndex.push(i);
		                return handlersIndex;
		            }
		        }
			}else if(args.length === 2){
				
				for(var i = handlers.length-1; i >= 0; i--) {
		            var handlerId = handlers[i];        // get handler id
		            var h = w._allHandlers[handlerId];  // get handler info
		            // If handler info matches type and handler function, we found it.
		            if (h.eventType == eventType){
		                handlersIndex.push(i);
		            }
		        }
		        if(handlersIndex.length>0){
		        	return handlersIndex;
		        }
				
			}else if(args.length === 1){

				for(var i = handlers.length-1; i >= 0; i--) {
		            handlersIndex.push(i);
		        }
		        if(handlersIndex.length>0){
		        	return handlersIndex;
		        }
			}
	        
	        
	        
	        
	        
	        
	        return -1;  // No match found
	    };
	
	    $E._removeAllEvents = function( ) {
	        // This function is registered as the onunload handler with
	        // attachEvent. This means that the this keyword refers to the
	        // window in which the event occurred.
	        var id,
	        	w = this;
	
	        // Iterate through all registered handlers
	        for(id in w._allHandlers) {
	            // Get handler info for this handler id
	            var h = w._allHandlers[id];
	            // Use the info to detach the handler
	            h.element.detachEvent("on" + h.eventType, h.wrappedEvent);
				h.element._handlers=null;
	            // Delete the handler info from the window
	            delete w._allHandlers[id];
	        }
	    }
	
	    // Private utility to generate unique handler ids
	    $E._counter = 0;
	    $E._uid = function(){
	    	return "h" + $E._counter++;
	    };
	}
	
	
	
	
	
	
	
	
	
	/**
	 * 
	 * 文档加载完成时事件监听器
	 * 
	 * @method onDomReady
	 * @memberOf event
	 * 
	 * @param element 元素
	 * @param eventType 事件类型，不含on
	 * @param handler 事件处理器
	 */
	onDomReady = function( f ) {
		// If the DOM is already loaded, execute the function right away
	    if ( onDomReady.done ) {
	    	return f();
	    }
	
	    // If we’ve already added a function
	    if ( onDomReady.timer ) {
	        // Add it to the list of functions to execute
	        onDomReady.ready.push( f );
	    } else {
	        // 初始化onDomReady后要执行的function的数组
	        onDomReady.ready = [ f ];
	        
	        // Attach an event for when the page finishes  loading,
	        // just in case it finishes first. Uses addEvent.
	        $E.on(window, "load", isDomReady);
	
	        //  Check to see if the DOM is ready as quickly as possible
	        onDomReady.timer = window.setInterval( isDomReady, 300 );
	    }
	}
	
	/**
	 * 
	 * 判断文档加载是否完成
	 * 
	 * @method isDomReady
	 * @memberOf event
	 * 
	 * @param element 元素
	 * @param eventType 事件类型，不含on
	 * @param handler 事件处理器
	 */
	// Checks to see if the DOM is ready for navigation
	isDomReady = function() {
	    // If we already figured out that the page is ready, ignore
	    if ( onDomReady.done ) {
	    	return true;
	    }
	
	    // Check to see if a number of functions and elements are
	    // able to be accessed
	    if ( document && document.getElementsByTagName && document.getElementById && document.body ) {
	    	// Remember that we’re now done
			onDomReady.done = true;
			
	        // If they’re ready, we can stop checking
	        window.clearInterval( onDomReady.timer );
	        onDomReady.timer = null;
	
	        // Execute all the functions that were waiting
	        for ( var i = 0; i < onDomReady.ready.length; i++ ){
	            onDomReady.ready[i]();
	        }

	        onDomReady.ready = null;
	        
	        return true;
	    }
	}
	
	
	
	
	/**
	 * 创建一个消息源发布者的类
	 * 
	 * @class Publish
	 * @return {Object} 返回生成的消息源
	 * 
	 * @example
	 * Jet().$package(function(J){
	 * 	var onMsg = new J.Publish();
	 *  var funcA = function(option){
	 *  	alert(option);
	 *  };
	 *  // 注册一个事件的观察者
	 * 	onMsg.subscribe(funcA);
	 * 	var option = "demo";
	 * 	onMsg.deliver(option);
	 * 	onMsg.unsubscribe(funcA);
	 * 	onMsg.deliver(option);
	 * 	
	 * };
	 * 
	 */
	Publish = function(){
		this.subscribers = [];
	};
	
	/**
	 * 注册观察者
	 * @memberOf Publish.prototype
	 * @param {Function} func 要注册的观察者
	 * @return {Function} 返回结果
	 */
	Publish.prototype.subscribe = function(func){
		var alreadyExists = J.array.some(this.subscribers, function(el){
			return el === func;
		});
		if(!alreadyExists){
			this.subscribers.push(func);
		}
		return func;
	};
	
	/**
	 * 触发事件
	 * @memberOf Publish.prototype
	 * @param {Mixed} msg 要注册的观察者
	 * @return {Function} 返回结果
	 */
	Publish.prototype.deliver = function(msg){
		J.array.forEach(this.subscribers, function(fn){
			fn(msg);
		});
	};
	
	/**
	 * 注销观察者
	 * @memberOf Publish.prototype
	 * @param {Function} func 要注销的观察者
	 * @return {Function} 返回结果
	 */
	Publish.prototype.unsubscribe = function(func){
		this.subscribers = J.array.filter(this.subscribers, function(el){
			return el !== func;
		});
		return func;
	};
	
	
	
	
	
	
	
	
	/**
	 * 
	 * 为自定义Model添加事件监听器
	 * 
	 * @method addObserver
	 * @memberOf event
	 * 
	 * @param targetModel 目标 model，即被观察的目标
	 * @param eventType 事件类型，不含on
	 * @param handler 观察者要注册的事件处理器
	 */
	addObserver = function(targetModel, eventType, handler){
		var handlers,
			length,
			index,
			i;
		if(handler){
			
		
			// 转换成完整的事件描述字符串
			eventType = "on" + eventType;
			
			// 判断对象是否含有$events对象
			if(!!!targetModel._$events){
				targetModel._$events={};
			}
			
			// 判断对象的$events对象是否含有eventType描述的事件类型
			if(!targetModel._$events[eventType]){
				//若没有则新建
				targetModel._$events[eventType]=[];
			}
		
			handlers = targetModel._$events[eventType];
			length = handlers.length;
			index = -1;
		
			// 通过循环，判断对象的handlers数组是否已经含有要添加的handler
			for(i=0; i<length; i++){
				if(handlers[i] === handler){
					index = i;
					break;
				}		
			}
			// 如果没有找到，则加入此handler
			if(index === -1){
				handlers.push(handler);
				//alert(handlers[handlers.length-1])
			}
		}else{
			J.out(">>> 添加的观察者方法不存在："+targetModel+eventType+handler);
		}
	};
	/**
	 * 
	 * 批量为自定义Model添加事件监听器
	 * 
	 * @method addObservers
	 * @memberOf event
	 * 
	 * @param obj 目标 model，即被观察的目标
	 *     obj = { targetModel : {eventType:handler,eventType2:handler2...} , targetModel2: {eventType:handler,eventType2:handler2...}  }
	 */
	addObservers = function(obj){
		//TODO 这里的代码是直接复制addObserver的（为避免太多函数调用耗费栈）
		var t=obj['targetModel'];
		var m=obj['eventMapping'];
		for(var i in m){
			addObserver(t,i,m[i]);
		}
	
	};
	/**
	 * 
	 * 触发自定义Model事件的监听器
	 * 
	 * @method notifyObservers
	 * @memberOf event
	 * 
	 * @param targetModel 目标 model，即被观察目标
	 * @param eventType 事件类型，不含on
	 * @param options 触发的参数对象
	 * @return {Boolean} 是否出发到至少一个的观察者
	 */
	notifyObservers = function(targetModel, eventType, argument){
		//addInvokeTime(eventType);
		var handlers,
			i;
			
		eventType = "on" + eventType;
		var flag = true;
		if(targetModel._$events && targetModel._$events[eventType]){
			handlers = targetModel._$events[eventType];
			if(handlers.length > 0){
				// 通过循环，执行handlers数组所包含的所有函数function
				for(i=0; i<handlers.length; i++){
					if(handlers[i].apply(targetModel, [argument])){
						
					}else{
						flag = false;
					}
				}
				//return flag;
			}
		}else{
			// throw new Error("还没有定义 [" + targetModel + "] 对象的: " + eventType + " 事件！");
			return false;
		}
		return flag;
	};
	
	
	/**
	 * 
	 * 为自定义 Model 移除事件监听器
	 * 
	 * @method removeObserver
	 * @memberOf event
	 * 
	 * @param targetModel 目标 model，即被观察的目标
	 * @param eventType 事件类型，不含on
	 * @param handler 观察者要取消注册的事件处理器
	 */
	// 按照对象和事件处理函数来移除事件处理函数
	removeObserver = function(targetModel, eventType, handler){
		var i,
			j,
			handlers,
			length,
			events = targetModel._$events;
		if(handler){
			
			if(events){
				eventType = "on" + eventType;
				handlers = events[eventType];
				
				if(handlers){
					length = handlers.length;
					for(i=0; i<length; i++){
						if(handlers[i] == handler){
							handlers[i] = null;
							handlers.splice(i, 1);
							break;
						}	
					}
				}
				
				
			}
		}else if(eventType){
			if(events){
				eventType = "on" + eventType;
				handlers = events[eventType];
				if(handlers){
					length = handlers.length;
					for(i=0; i<length; i++){
						handlers[i] = null;
					}
					delete events[eventType];
				}
				
			}
			
		}else if(targetModel){
			if(events){
				for(i in events){
					delete events[i];
				}
				delete targetModel._$events;
			}
		}
	};
	
	$E.addEventListener = addEventListener;
	$E.removeEventListener = removeEventListener;
	// alias
	$E.on = $E.addEventListener;
	$E.off = $E.removeEventListener;
	
	$E.onDomReady = onDomReady;

	$E.Publish = Publish;
	
	// Model 事件方法
	$E.addObserver = addObserver;
	$E.addObservers = addObservers;
	$E.notifyObservers = notifyObservers;
	$E.removeObserver = removeObserver;
});